/*********************/
/* PrimeThreadsApp.m */
/*********************/
#import "PrimeThreadsApp.h"
#import <appkit/Button.h>
#import <appkit/Control.h>
#import <appkit/chunk.h>
#import <dpsclient/dpsNeXT.h>
#import <math.h>

/* the code is much smaller when you take out the comments ;-) */

/*
 * thePrimeList is maintained by the NXChunk system.  GROW_BY
 * is how much the structure's malloc'ed space will grow, when
 * it needs to be expanded.  The chunk is also initialized
 * with GROW_BY bytes.
 */  
#define GROW_BY 8192
/* every UPDATE_PERIOD seconds, a timed entry calls updateFieldsGlue */
#define UPDATE_PERIOD 0.25

struct primeList {
/* see /usr/include/appkit/chunk.h for a description of the NXChunk */
    NXChunk chunk;
/* number of primes in the list */
    int numPrimes;
/* This is just a bogus declaration. There can be any number of primes. */
    int thePrimes[2];
} *thePrimeList;

/* this mutex guards thePrimeList */
mutex_t primeListLock;

/* this is the function the timed entry calls */
void updateFieldsGlue(DPSTimedEntry te, double now, void *self)
/* we, in turn, call the updateFields method (we're just glue) */
{ [(id)self updateFields]; }

/* findPrimes is executed on its own thread.  it never stops. */
void findPrimes(int ignored)
{
    int counter, possiblePrime = 5, testLimit;
    BOOL isPrime;

    while (YES) /* repeat forever */
    {
/* isPrime starts at YES.  if an exact divisor is found, it becomes NO */
        isPrime = YES;
/* for any non-prime integer x, one of its factors is less than sqrt(x) */
        testLimit = sqrt(possiblePrime);
        for (counter=1; isPrime && (thePrimeList->thePrimes[counter]<=testLimit); counter++)
            if (possiblePrime % thePrimeList->thePrimes[counter] == 0)
                isPrime = NO;
        if (isPrime)
        {
/* lock the mutex before changing the list */
          mutex_lock(primeListLock);
/* another prime for the list! */
          thePrimeList->numPrimes++;
/*
 * grow the chunk.  usually this does nothing; sometimes more
 * space gets allocated.
 */
          thePrimeList =
            (struct primeList *)NXChunkGrow((NXChunk *)thePrimeList,
              thePrimeList->numPrimes * sizeof(int));
/* place our newly-certified prime into the list. */
          thePrimeList->thePrimes[thePrimeList->numPrimes - 1] = possiblePrime;
/* unlock the mutex now that we're done with the list */
          mutex_unlock(primeListLock);
        }
        possiblePrime += 2; /* don't bother with even numbers */
/* tell the scheduler that this is a convenient time to run other threads */
        cthread_yield();
    }
}

@implementation PrimeThreadsApp

/* IB outlets */
- setNumPrimesField:anObject
{ numPrimesField = anObject; return self; }
- setHighestPrimeField:anObject
{ highestPrimeField = anObject; return self; }

- appDidInit:sender
{
   /* allocate the mutex */
    primeListLock = mutex_alloc();
   /*
    * initialize thePrimeList with GROW_BY bytes of memory
    * to start with, and make it grow by GROW_BY bytes when
    * it expands.
    */
    thePrimeList = (struct primeList *)NXChunkMalloc(GROW_BY, GROW_BY);
   /* we start the list with 2 primes: 2 and 3. */
    thePrimeList->numPrimes = 2;
    thePrimeList->thePrimes[0] = 2;
    thePrimeList->thePrimes[1] = 3;
   /*
    * start up the primeFinderThread. It will never stop, but we can
    * suspend and resume the thread, which is just as good.
    */
    primeFinderThread = cthread_fork(findPrimes, 0);
   /* since we will never want to join the thread, we detach it. */
    cthread_detach(primeFinderThread);
   /* create the timed entry that will handle updating the fields */
    DPSAddTimedEntry(UPDATE_PERIOD, updateFieldsGlue,
      (void *)self, NX_BASETHRESHOLD);
    return self;
}

- toggleGoStop:sender
{
/*
 * cthread_thread gets us the actual thread, which is a part of
 * the cthread structure.  struct cthread has a lot of other neat
 * stuff in it, too, but we don't (explicitly) use any of it.
 * thread_resume() and thread_suspend() just want the thread identifier,
 * not a cthread, and we give it to 'em.
 */
    if ([sender state] == YES) /* turn it on */
        thread_resume(cthread_thread(primeFinderThread));
    else /* turn it off */
        thread_suspend(cthread_thread(primeFinderThread));
    return self;
}

- updateFields
{
/* lock the mutex before reading the list */
    mutex_lock(primeListLock);
    [numPrimesField setIntValue:thePrimeList->numPrimes];
    [highestPrimeField setIntValue:thePrimeList->thePrimes[thePrimeList->numPrimes - 1]];
/* unlock the mutex, now that we're done with the list */
    mutex_unlock(primeListLock);
    return self;
}

@end
